#pragma once

#include "vector3.h"
#include "vector2.h"
#include "matrix33.h"
#include <vector>
#include <Glut/glut.h>

using namespace Math;
using namespace std;

//////////////////////////////////////////////////////////////////////////////
// Forward Declarations
//////////////////////////////////////////////////////////////////////////////
class TriEdge;
class TriFace;
class TriFaceRender;


//////////////////////////////////////////////////////////////////////////////
// Types
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
// TriVertex
//
// Description:
//      Vertex class for the triangular mesh.
//////////////////////////////////////////////////////////////////////////////
class TriVertex
{
public:
   bool azul;

protected:

   int id;

   bool marked;

   // Geometry info
   Vector3 coords0;
   Vector3 coords;
   Vector3 coordsOld;
   Vector3 normal;
   Vector2 textureCoords; // uv
   Real area0;

   // Topology info
   TriEdge* edge;
   int valence;
   bool boundary;
   bool feature;
   bool corner;

   // Color info
   GLfloat dispProperty;

   // Ownership
   TriFace* owner;

   // Collision time
   Real collTime;

   Real deformationDensity;
   Real deformationDensityPowAlpha;
   
public:

   //Constructors and destructors
   TriVertex(void)
   {
      Initialize();
   }

   ~TriVertex(void);

   void Initialize(void);

   void Copy(TriVertex* v) 
   {
      SetId(v->Id());
      if (v->Marked()) Mark();
      SetFeature(v->IsFeature());
      SetCorner(v->IsCorner());

      SetCoords0(v->PositionInit());
      SetNormal(v->Normal());

      SetAdjEdge(v->AdjEdge());
      SetValence(v->Valence());   
      if (v->IsOnBoundary()) SetBoundary();

      SetOwner(v->Owner());
   }

   // Get functions
   int Id(void) const
   {
      return id;
   }

   const bool Corner(void) const
   {
      return corner;
   }

   const Vector3& PositionInit(void) const
   {
      return coords0;
   }

   const Vector3& PositionOld(void) const
   {
      return coordsOld;
   }

   void PositionInit(Real x,Real y,Real z)
   {
      coords0.set(x,y,z);
   }

   void PositionOld(Real x,Real y,Real z)
   {
      coordsOld.set(x,y,z);
   }

   

   const Vector3& Position(void) const
   {
      return coords;
   }

   void Position(Real x, Real y, Real z)
   {
       coords.x(x);
       coords.y(y);
       coords.z(z);
   }

   void UpdatePosition(void);

   void UpdatePositionOld(void)
   {
      coordsOld = coords;
   }

   const Vector3& Normal(void) const
   {
      return normal;
   }

   const Vector2& TextureCoord(void) const
   {
      return textureCoords;
   }

   TriEdge* AdjEdge(void) const
   {
      return edge;
   }

   bool Marked(void) const
   {
      return marked;
   }

   bool IsFeature(void) const
   {
      return feature;
   }

   bool IsCorner(void) const
   {
      return corner;
   }

   TriFace* Owner(void) const
   {
      return owner;
   }

   Real CollisionTime(void) const
   {
      return collTime;
   }

   bool IsGrasped(void) const
   {
      return false;
   }

   GLfloat DisplayProperty(void) const
   {
      return dispProperty;
   }

   Real Area0(void) const
   {
      return area0;
   }

   // Set functions
   void SetId(int i)
   {
      id=i;
   }

   void SetCoords0(const Vector3& p)
   {
      coords = coordsOld = coords0 = p;
   }

   void SetCoords(const Vector3& p)
   {
      coords = p;
   }

   void SetNormal(const Vector3& n)
   {
      normal = n;
   }

   void SetTextureCoords (const Vector2& uv)
   {
      textureCoords = uv;
   }

   void SetAdjEdge(TriEdge* e)
   {
      edge = e;
   }

   void Mark(void)
   {
      marked=true;
   }

   void Unmark(void)
   {
      marked=false;
   }

   void SetFeature(bool f)
   {
      feature=f;
   }

   void SetCorner(bool c)
   {
      corner=c;
   }

   void SetBoundary(void)
   {
      boundary=true;
   }

   void SetValence(int val)
   {
      valence = val;
   }

   void SetOwner(TriFace *f)
   {
      owner = f;
   }

   void SetCollisionTime(Real t)
   {
      collTime = t;
   }

   GLfloat& DisplayProperty(void)
   {
      return dispProperty;
   }

   // Topology functions
   bool ValenceComputed(void) const
   {
      return valence>0;
   }

   int Valence(void)
   {
      return ValenceComputed()?valence:ComputeValence();
   }

   int ComputeValence(void);

   TriEdge* AdjEdge(TriVertex* v) const;

   TriFace* AdjFace(TriVertex* v) const;

   bool IsAdjacentTo(const TriFace* f) const;

#ifdef _DEBUG
   bool VerifyTopology(int pos) const;
#endif

   bool IsOnBoundary(void) const
   {
      return boundary;
   }

   //Get ids of all incident faces
   vector<int>* GetIncidentFaces(void) const;

   vector<TriFace*> GetFaces(void) const;

   // Geometry functions
   void ResetNormal(void)
   {
      normal = Vector3::ZERO;
   }

   void AddToNormal(const Vector3& n)
   {
      normal += n;
   }

   void NormalizeNormal(void)
   {
      normal.normalize();
   }

   void ResetArea(void)
   {
      area0 = 0.0;
   }

   void AddToArea(Real a)
   {
      area0 += a;
   }

   void NormalizeArea(void)
   {
      area0 /= Valence();
   }

   //OpenGL related functions
   void Draw(void) const;
   void Draw(TriFaceRender *properties) const;

   //Interpolation from deformation field

   bool IsFixed(void) const;

   Matrix33 GetLocalReferenceSystem(void); //Create new Local Reference System

   Real GetDeformationDensity() const { return deformationDensity; }
   Real GetDeformationDensityPowAlpha() const { return deformationDensityPowAlpha; }
   void SetDeformationDensity(Real val, Real exponent)
   { 
      deformationDensity = val; 
      deformationDensityPowAlpha = powf(val,exponent);
   }
};



